package br.edu.frb.lista;

/**
 * @author Junior
 * @author joelamalio
 */
public class ListaDuplamenteEncadeada<T> extends ListaDuplamenteEncadeadaAb<T> implements Lista<T> {

    //<editor-fold defaultstate="collapsed" desc="Atributos">
    private int tamanho = 0;
    private Node<T> inicio;
    private Node<T> fim;
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Contrutor">
    public ListaDuplamenteEncadeada() {
        tamanho = 0;
        inicio = new Node<T>(null, null, null);
        fim = new Node<T>(null, null, null);
        inicio.setProximo(fim);
        inicio.setAnterior(fim);
        fim.setProximo(inicio);
        fim.setAnterior(inicio);
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Metodos implementados">
    public void inserirNoInicio(T item) {
        Node<T> temp = new Node<T>(item);
        addDepois(inicio, temp);
    }

    public void inserirNoFim(T item) {
        Node<T> temp = new Node<T>(item);
        addAntes(fim, temp);
    }

    public T removerDoInicio() {
        if (vazio()) {
            throw new RuntimeException("Lista Vazia.");
        }
        tamanho--;
        Node<T> temp = getProximo(inicio);
        inicio.setProximo(temp.getProximo());
        temp.getProximo().setAnterior(inicio);
        return temp.getValor();
    }

    public T removerDoFinal() {
        if (vazio()) {
            throw new RuntimeException("Lista Vazia.");
        }
        tamanho--;
        Node<T> temp = getAnterior(fim);
        fim.setAnterior(temp.getAnterior());
        temp.getAnterior().setProximo(fim);
        return temp.getValor();
    }

    public void inserirEm(T item, int posicao) {
        Node<T> temp = buscarNode(posicao);
        addAntes(temp, new Node<T>(item));
    }

    public int buscar(T item) {
        if (vazio()) {
            throw new RuntimeException("Lista Vazia.");
        }
        if (item == null) {
            throw new RuntimeException("Nulo.");
        }

        Node<T> temp = inicio.getProximo();
        int pos = 0;
        while (temp != fim && !item.equals(temp.getValor())) {
            temp = temp.getProximo();
            pos++;
        }
        if (temp == fim) {
            return -1;
        }
        return pos;
    }

    public T buscar(int posicao) {
        return buscarNode(posicao).getValor();
    }

    public void remover(T item) {
        if (vazio()) {
            throw new RuntimeException("Lista Vazia.");
        }
        Node<T> temp = buscarNode(item);
        Node<T> anterior = getAnterior(temp);
        Node<T> proximo = getProximo(temp);
        anterior.setProximo(proximo);
        proximo.setAnterior(anterior);
        tamanho--;
    }

    public Object[] listar() {
        if (vazio()) {
            throw new RuntimeException("Lista Vazia.");
        }
        Object[] array = new Object[tamanho];
        for (int i = 0; i < array.length; i++) {
            Node<T> item = buscarNode(i);
            array[i] = item.getValor();
        }
        return array;
    }

    public int tamanho() {
        return tamanho;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Privados">
    private boolean vazio() {
        return tamanho == 0;
    }

    private void addDepois(Node<T> item, Node<T> no) {
        Node<T> proximo = getProximo(item);
        item.setProximo(no);
        proximo.setAnterior(no);
        no.setProximo(proximo);
        no.setAnterior(item);
        tamanho++;
    }

    private void addAntes(Node<T> item, Node<T> no) {
        Node<T> anterior = getAnterior(item);
        no.setAnterior(anterior);
        no.setProximo(item);
        anterior.setProximo(no);
        item.setAnterior(no);
        tamanho++;
    }

    private Node<T> getAnterior(Node<T> no) {
        return no.getAnterior();
    }

    private Node<T> getProximo(Node<T> no) {
        return no.getProximo();
    }

    private Node<T> buscarNode(int posicao) {
        if (vazio()) {
            throw new RuntimeException("Lista Vazia.");
        }
        if (posicao < 0 || posicao + 1 > tamanho) {
            throw new RuntimeException("Indice fora dos limites.");
        }
        Node<T> temp = inicio.getProximo();
        for (int i = 0; i < posicao; i++) {
            temp = temp.getProximo();
        }
        return temp;
    }

    private Node<T> buscarNode(T item) {
        if (vazio()) {
            throw new RuntimeException("Lista Vazia.");
        }
        if (item == null) {
            throw new RuntimeException("Nulo.");
        }
        Node<T> temp = inicio.getProximo();
        while (temp != fim && !item.equals(temp.getValor())) {
            temp = temp.getProximo();
        }
        if (temp == fim) {
            return null;
        }
        return temp;
    }
    //</editor-fold>
}
